package com.ecp.commons.file;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpUtils;

import net.xinshi.icfs.client.IFileSystem;

import com.ecp.commons.Environment;

@SuppressWarnings("deprecation")
public class MultipartRequest {

	private static final int			DEFAULT_MAX_POST_SIZE	= 8 * 1024 * 1024;

	protected Hashtable<Object, Object>	parameters				= new Hashtable<Object, Object>();
	protected Hashtable<Object, Object>	files					= new Hashtable<Object, Object>();
	protected Map<String, Object>		alias					= new ConcurrentHashMap<String, Object>();
	//private String tempFileName;
	private int							fileSize				= 0;
	private static String				DEFAULT_ENCODING		= "ISO-8859-1";

	/**
	 * preferred encoding
	 */
	private String						encoding				= DEFAULT_ENCODING;

	String								uploadPath;

	public MultipartRequest(HttpServletRequest request, String saveDirectory) throws IOException {
		this(request, saveDirectory, DEFAULT_MAX_POST_SIZE);
	}

	public MultipartRequest(HttpServletRequest request, String saveDirectory, int maxPostSize) throws IOException {
		this(request, saveDirectory, maxPostSize, null, null);
	}

	public MultipartRequest(HttpServletRequest request, String saveDirectory, String encoding) throws IOException {
		this(request, saveDirectory, DEFAULT_MAX_POST_SIZE, encoding, null);
	}

	public MultipartRequest(HttpServletRequest request, String saveDirectory, int maxPostSize, FileRenamePolicy policy)
			throws IOException {
		this(request, saveDirectory, maxPostSize, null, policy);
	}

	public MultipartRequest(HttpServletRequest request, String saveDirectory, int maxPostSize, String encoding)
			throws IOException {
		this(request, saveDirectory, maxPostSize, encoding, null);
	}

	public MultipartRequest(HttpServletRequest request, String saveDirectory, int maxPostSize, String encoding,
			FileRenamePolicy policy) throws IOException {
		if (request == null)
			throw new IllegalArgumentException("request cannot be null");
		if (saveDirectory == null)
			throw new IllegalArgumentException("directory cannot be null.");
		if (maxPostSize <= 0) {
			throw new IllegalArgumentException("Max size should be int value.");
		}
		uploadPath = saveDirectory;
		int requestSize = request.getContentLength();
		if (requestSize > maxPostSize) {
			throw new IOException("File exceed (" + maxPostSize + " bytes)");
		}
		setFileSize(requestSize);
		File dir = new File(saveDirectory);
		if (!dir.isDirectory())
			dir.mkdirs();
		if (!dir.canWrite())
			throw new IllegalArgumentException("Cannot write to : " + saveDirectory);
		if (encoding != null) {
			this.encoding = encoding;
		}
		MultipartParser parser = new MultipartParser(request, maxPostSize, true, true, encoding);
		if (request.getQueryString() != null) {
			@SuppressWarnings({ "unchecked" })
			Hashtable<Object, Object> queryParameters = HttpUtils.parseQueryString(request.getQueryString());
			// For our own use, name it a name->Vector structure
			Enumeration<Object> queryParameterNames = queryParameters.keys();
			while (queryParameterNames.hasMoreElements()) {
				Object paramName = queryParameterNames.nextElement();
				String[] values = (String[]) queryParameters.get(paramName);
				Vector<String> newValues = new Vector<String>();
				for (int i = 0; i < values.length; i++) {
					newValues.add(values[i]);
				}
				parameters.put(paramName, newValues);
			}
		}
		Part part;
		while ((part = parser.readNextPart()) != null) {
			String name = part.getName();
			if (part.isParam()) {
				// It's a parameter part, add it to the vector of values
				ParamPart paramPart = (ParamPart) part;
				String value = paramPart.getStringValue();
				@SuppressWarnings("unchecked")
				Vector<String> existingValues = (Vector<String>) parameters.get(name);
				if (existingValues == null) {
					existingValues = new Vector<String>();
					parameters.put(name, existingValues);
				}
				existingValues.addElement(value);
			} else if (part.isFile()) {
				// It's a file part
				FilePart filePart = (FilePart) part;
				String fileName = filePart.getFileName();

				if (fileName != null) {
					String type = null;

					if (type != null) {
						String[] types = type.trim().split("\\|");
						boolean matches = false;
						String fileNameTemp = fileName.toUpperCase();
						for (int i = 0; i < types.length; i++) {
							String ext = (".*\\." + types[i]).toUpperCase();
							if (fileNameTemp.matches(ext)) {
								matches = true;
								break;
							}
						}
						if (!matches) {
							throw new IllegalArgumentException("Not supported file type.");
						}
					}

					filePart.setRenamePolicy(policy); // null policy is OK
					// The part actually contained a file
					filePart.writeTo(dir);
					//setTempFileName(filePart.getTempFileName());
					alias.put(name, filePart.getTempFileName());
					UploadedFile uploadFile = new UploadedFile(dir.toString(), filePart.getTempFileName(), fileName,
							filePart.getContentType());
					files.put(name, uploadFile);

					try {
						IFileSystem fileSystem = (IFileSystem) Environment.getEnv().getBean("uploadFileSystem");
						if (fileSystem != null) {
							File f = uploadFile.getFile();
							String destFile = this.getRelativeUrl(uploadFile);
							try {
								// System.out.println("Going to submit ...");
								fileSystem.submitFile(f, destFile);
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
					}

				} else {
					// The field did not contain a file
					files.put(name, new UploadedFile(null, null, null, null));
					alias.put(name, "");
				}
			}
		}
	}

	public MultipartRequest(ServletRequest request, String saveDirectory) throws IOException {
		this((HttpServletRequest) request, saveDirectory);
	}

	public MultipartRequest(ServletRequest request, String saveDirectory, int maxPostSize) throws IOException {
		this((HttpServletRequest) request, saveDirectory, maxPostSize);
	}

	public Enumeration<Object> getParameterNames() {
		return parameters.keys();
	}

	public Enumeration<Object> getFileNames() {
		return files.keys();
	}

	public String getParameter(String name) {
		try {
			@SuppressWarnings("unchecked")
			Vector<String> values = (Vector<String>) parameters.get(name);
			if (values == null || values.size() == 0) {
				return null;
			}
			String value = (String) values.elementAt(values.size() - 1);
			if (value != null)
				value = new String(value.getBytes(this.encoding), "UTF-8");
			return value;
		} catch (Exception e) {
			return null;
		}
	}

	public void setParameter(String name, String value) {
		try {
			Vector<String> newValues = new Vector<String>();
			newValues.add(value);
			parameters.put(name, newValues);
		} catch (Exception e) {
		}
	}

	public String[] getParameterValues(String name) {
		try {
			@SuppressWarnings("unchecked")
			Vector<String> values = (Vector<String>) parameters.get(name);
			if (values == null || values.size() == 0) {
				return null;
			}
			String[] valuesArray = new String[values.size()];
			values.copyInto(valuesArray);
			return valuesArray;
		} catch (Exception e) {
			return null;
		}
	}

	public String getFilesystemName(String name) {
		try {
			UploadedFile file = (UploadedFile) files.get(name);
			return file.getFilesystemName(); // may be null
		} catch (Exception e) {
			return null;
		}
	}

	public String getRelativeUrl(UploadedFile file) {
		if (file == null || file.getFilesystemName() == null) {
			return null;
		}
		File f = file.getFile();
		String AbsoluteUrl = f.getAbsolutePath();
		if (AbsoluteUrl.startsWith(uploadPath)) {
			String RelUrl = AbsoluteUrl.substring(uploadPath.length());
			String re = RelUrl.replaceAll("\\\\", "/");
			if (re.startsWith("/")) {
				re = re.substring(1);
			}
			return re;

		} else {
			return file.getFilesystemName();
		}
	}

	public String getRelativeUrl(String name) {

		IFileSystem ifilesystem = (IFileSystem) Environment.getEnv().getBean("uploadFileSystem");
		if (ifilesystem == null) {
			UploadedFile file = (UploadedFile) files.get(name);
			return getRelativeUrl(file);
		} else {
			UploadedFile file = (UploadedFile) files.get(name);
			String localPath = getRelativeUrl(file);
			return ifilesystem.getGlobalPath(localPath);
		}
	}

	public String getOriginalFileName(String name) {
		try {
			UploadedFile file = (UploadedFile) files.get(name);
			return file.getOriginalFileName(); // may be null
		} catch (Exception e) {
			return null;
		}
	}

	public String getContentType(String name) {
		try {
			UploadedFile file = (UploadedFile) files.get(name);
			return file.getContentType(); // may be null
		} catch (Exception e) {
			return null;
		}
	}

	public File getFile(String name) {
		try {
			UploadedFile file = (UploadedFile) files.get(name);
			return file.getFile(); // may be null
		} catch (Exception e) {
			return null;
		}
	}

	public int getFileSize() {
		return fileSize;
	}

	public void setFileSize(int fileSize) {
		this.fileSize = fileSize;
	}

	public Map<String, Object> getAlias() {
		return alias;
	}

	public void setAlias(Map<String, Object> alias) {
		this.alias = alias;
	}
}

class UploadedFile {

	protected String	dir;
	private String		filename;
	private String		original;
	private String		type;

	UploadedFile(String dir, String filename, String original, String type) {
		this.dir = dir;
		this.filename = filename;
		this.original = original;
		this.type = type;
	}

	public String getContentType() {
		return type;
	}

	public String getFilesystemName() {
		if (filename == null)
			return null;
		else {
			File f = new File(filename);
			return f.getName();
		}
	}

	public String getOriginalFileName() {
		return original;
	}

	public File getFile() {
		if (filename == null)
			return null;
		return new File(filename);
	}
}
